using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using System.Windows.Input;

namespace ZoomAtMouse
{
    ///<summary>
    ///TestExtension
    ///</summary>
    public class MouseExtension : MouseProcessorBase
    {
        IWpfTextView _view;

        public MouseExtension(IWpfTextView view)
        {
            _view = view;
            _view.LostAggregateFocus += new System.EventHandler(_view_LostAggregateFocus);
        }

        void _view_LostAggregateFocus(object sender, System.EventArgs e)
        {
            Keyboard.ResetKeyStates();
        }
        
        public override void PostprocessMouseLeave(MouseEventArgs e)
        {
            Keyboard.ResetKeyStates();
        }

        public override void PreprocessMouseWheel(MouseWheelEventArgs e)
        {
            if (Settings.ScrollOverrideEnabled)
            {
                //do custom scroll functionality (only if we are not doing custom zoom functionality which does its own scrolling)
                if (!(IsJumpMouseZoomModifiers() || IsMouseZoomModifiers()) && _view.VisualElement.IsMouseOver)
                {
                    double pixels = _view.ViewportHeight * (double)(Settings.ScrollOverridePercentage / 100);
                    _view.ViewScroller.ScrollViewportVerticallyByPixels(e.Delta < 0 ? -pixels : pixels);
                    e.Handled = true;
                }
            }
        }

        public override void PostprocessMouseWheel(System.Windows.Input.MouseWheelEventArgs e)
        {
            if (IsJumpMouseZoomModifiers())
            {
                if (e.Delta < 0)
                {
                    if (_view.ZoomLevel > 100) _view.ZoomLevel = 100;
                    else _view.ZoomLevel = 20;
                }
                else
                {
                    if (_view.ZoomLevel < 100) _view.ZoomLevel = 100;
                    else _view.ZoomLevel = 400;
                }

                PostZoom();
                e.Handled = true;
            }
            else if (IsMouseZoomModifiers())
            {
                if (e.Delta < 0)
                {
                    _view.ZoomLevel *= (100.0 / 110.0);
                }
                else
                {
                    _view.ZoomLevel *= 1.1;
                }

                PostZoom();
                e.Handled = true;
            }
        }

        public override void PreprocessMouseDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            if (IsResetZoomModifiers() &&
                e.MiddleButton == System.Windows.Input.MouseButtonState.Pressed)
            {
                //return to 100% zoom
                _view.ZoomLevel = 100;

                PostZoom();
            }
        }

        private void PostZoom()
        {
            if (_view.ZoomLevel != Keyboard.ZoomLevelAtCtrlKeyDown)
            {
                Point mousePosition = Mouse.GetPosition(_view.VisualElement);

                //The amount of zoom change
                double zoomRatio = _view.ZoomLevel == 0 ? 0 :
                    1.0 - (Keyboard.ZoomLevelAtCtrlKeyDown / _view.ZoomLevel);

                //The position that the old zoom point is after the built in zoom has finished
                double newY = mousePosition.Y * (1.0 + zoomRatio);

                //The amount to scroll the y axis in order to move the zoom point back to the mouse position
                double yPixelOffset = mousePosition.Y - newY;

                //HACK: the view does something non-standard when zoomed to 100%
                //so here we compensate
                double noZoomAdjustmentRatio = 0.33;
                double noZoomAdjustment = yPixelOffset * noZoomAdjustmentRatio * (_view.ZoomLevel < Keyboard.ZoomLevelAtCtrlKeyDown ? -1 : 1);
                if (_view.ZoomLevel == 100)
                    yPixelOffset -= noZoomAdjustment;
                else if (Keyboard.ZoomLevelAtCtrlKeyDown == 100)
                    yPixelOffset += noZoomAdjustment;

                _view.ViewScroller.ScrollViewportVerticallyByPixels(yPixelOffset);

                Keyboard.ZoomLevelAtCtrlKeyDown = _view.ZoomLevel;
            }
        }

        private bool IsJumpMouseZoomModifiers()
        {
            return IsMouseZoomModifiers() && Keyboard.ShiftKeyDown;
        }

        private bool IsMouseZoomModifiers()
        {
            return Keyboard.CtrlKeyDown;
        }

        private bool IsResetZoomModifiers()
        {
            return Keyboard.CtrlKeyDown;
        }
    }
}
